import {
  AfterContentInit,
  ChangeDetectorRef,
  Component,
  ElementRef,
  HostListener,
  Input,
  Renderer2,
  ViewChild,
  Host,
  Optional,
  SimpleChanges,
  HostBinding,
  OnInit,
  OnDestroy,
  OnChanges,
  ChangeDetectionStrategy,
  ViewEncapsulation,
  ContentChildren, QueryList, ViewRef, NgZone, Inject,

} from '@angular/core';

import { ApesUpdateHostClassService } from '../core/services/update-host-class.service';
import { isEmpty } from '../core/util/check';
import { InputBoolean } from '../core/util/convert';
import { APES_WAVE_GLOBAL_CONFIG, ApesWaveConfig, ApesWaveDirective } from '@shared/apes/core/wave';
import { Subject } from 'rxjs';
import { ApesIconDirective } from '@shared/apes/icon';
import {
  ApesConfigService,
  ApesSizeLDSType,
  ApesSizeMap,
  findFirstNotEmptyNode,
  findLastNotEmptyNode, trimComponentName
} from '@shared/apes/core';
import { ContentObserver } from '@angular/cdk/observers';
import { ANIMATION_MODULE_TYPE } from '@angular/platform-browser/animations';
import { startWith, takeUntil } from 'rxjs/operators';
export type ApesButtonType = 'primary' | 'dashed' | 'danger' | 'default' | 'link';
export type ApesButtonShape = 'circle' | 'round' | null;


@Component({
  selector           : '[apes-button]',
  exportAs: 'apesButton',
  providers          : [ ApesUpdateHostClassService ],
  preserveWhitespaces: false,
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
  template           : `
    <i apes-icon apesType="loading" *ngIf="apesLoading"></i>
    <span #contentElement>
      <ng-content></ng-content>
    </span>
  `
})
export class  ApesButtonComponent implements AfterContentInit, OnInit, OnDestroy, OnChanges {
  prefixCls = 'apes-btn';
  readonly el: HTMLElement = this.elementRef.nativeElement;
  private iconElement: HTMLElement;
  private iconOnly = false;
  private destroy$ = new Subject();
  @ViewChild('contentElement', { static: true }) contentElement: ElementRef;
  @ContentChildren(ApesIconDirective, { read: ElementRef }) listOfIconElement: QueryList<ElementRef>;
  @HostBinding('attr.apes-wave') apesWave = new ApesWaveDirective(
    this.ngZone,
    this.elementRef,
    this.waveConfig,
    this.animationType
  );
  @Input() @InputBoolean() apesBlock = false;
  @Input() @InputBoolean() apesGhost = false;
  @Input() @InputBoolean() apesSearch = false;
  @Input() @InputBoolean() apesLoading = false;
  @Input() apesType: ApesButtonType = 'default';
  @Input() apesShape: ApesButtonShape = null;
  @Input() apesSize: ApesSizeLDSType = 'default';



  setClassMap(): void {
   const sizeMap: ApesSizeMap = { large: 'lg', small: 'sm' };
    this.apesUpdateHostClassService.updateHostClass(this.el, {
      [`${this.prefixCls}-${this.apesType}`]: this.apesType,
      [`${this.prefixCls}-${this.apesShape}`]: this.apesShape,
      [`${this.prefixCls}-${sizeMap[this.apesSize]}`]: sizeMap[this.apesSize],
      [`${this.prefixCls}-loading`]: this.apesLoading,
      [`${this.prefixCls}-icon-only`]: this.iconOnly,
      [`${this.prefixCls}-background-ghost`]: this.apesGhost,
      [`${this.prefixCls}-block`]: this.apesBlock,
      [`apes-input-search-button`]: this.apesSearch
    });
  }
  updateIconDisplay(value: boolean): void {
    if (this.iconElement) {
      this.renderer.setStyle(this.iconElement, 'display', value ? 'none' : 'inline-block');
    }
  }

  checkContent(): void {
    const hasIcon = this.listOfIconElement && this.listOfIconElement.length;
    if (hasIcon) {
      this.moveIcon();
    }
    this.renderer.removeStyle(this.contentElement.nativeElement, 'display');
    /** https://github.com/angular/angular/issues/12530 **/
    if (isEmpty(this.contentElement.nativeElement)) {
      this.renderer.setStyle(this.contentElement.nativeElement, 'display', 'none');
      this.iconOnly = !!hasIcon;
    } else {
      this.renderer.removeStyle(this.contentElement.nativeElement, 'display');
      this.iconOnly = false;
    }
    this.setClassMap();
    this.updateIconDisplay(this.apesLoading);
    if (!(this.cdr as ViewRef).destroyed) {
      this.cdr.detectChanges();
    }
  }

  moveIcon(): void {
    if (this.listOfIconElement && this.listOfIconElement.length) {
      const firstChildElement = findFirstNotEmptyNode(this.contentElement.nativeElement);
      const lastChildElement = findLastNotEmptyNode(this.contentElement.nativeElement);
      if (firstChildElement && firstChildElement === this.listOfIconElement.first.nativeElement) {
        this.renderer.insertBefore(this.el, firstChildElement, this.contentElement.nativeElement);
        this.iconElement = firstChildElement as HTMLElement;
      } else if (lastChildElement && lastChildElement === this.listOfIconElement.last.nativeElement) {
        this.renderer.appendChild(this.el, lastChildElement);
      }
    }
  }

  constructor(
    private elementRef: ElementRef,
    private cdr: ChangeDetectorRef,
    private renderer: Renderer2,
    private contentObserver: ContentObserver,
    private apesUpdateHostClassService: ApesUpdateHostClassService,
    private ngZone: NgZone,
    private apesConfigService: ApesConfigService,
    @Optional() @Inject(APES_WAVE_GLOBAL_CONFIG) private waveConfig: ApesWaveConfig,
    @Optional() @Inject(ANIMATION_MODULE_TYPE) private animationType: string
  ) {
    this.renderer.addClass(elementRef.nativeElement, 'apes-btn');
    this.apesConfigService
      .getConfigChangeEventForComponent(trimComponentName(this.constructor.name))
      .pipe(takeUntil(this.destroy$))
      .subscribe(() => {
        this.setClassMap();
        this.cdr.markForCheck();
      });
  }

  ngAfterContentInit(): void {
    this.contentObserver
      .observe(this.contentElement)
      .pipe(
        startWith(true),
        takeUntil(this.destroy$)
      )
      .subscribe(() => {
        // https://github.com/NG-ZORRO/ng-zorro-antd/issues/3079
        Promise.resolve().then(() => this.checkContent());
      });
  }

  ngOnInit(): void {
    this.setClassMap();
    this.apesWave.ngOnInit();
  }

  ngOnDestroy(): void {
    this.destroy$.next();
    this.destroy$.complete();
    this.apesWave.ngOnDestroy();
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (
      changes.apesBlock ||
      changes.apesGhost ||
      changes.apesSearch ||
      changes.apesType ||
      changes.apesShape ||
      changes.apesSize ||
      changes.apesLoading
    ) {
      this.setClassMap();
    }
    if (changes.apesLoading) {
      this.updateIconDisplay(this.apesLoading);
    }
    if (changes.nzType && changes.nzType.currentValue === 'link') {
      this.apesWave.disable();
    } else {
      this.apesWave.enable();
    }
  }
}
